<?php

namespace DeCMF\Core\Db;

use DeCMF\Core\Model as BaseModel;
use DeCMF\Core\Db\Query;

/**
 * Db Model
 * 实现了ORM和ActiveRecords模式
 */
class Model extends BaseModel
{
        
    /**
     *
     * @var string 主键名称
     */
    protected $pk = 'id';
    
    /**
     *
     * @var boolean 主键是否自动增长
     */
    protected $autoinc = false;
    
    /**
     *
     * @var string|null 数据表前缀
     */
    protected $tablePrefix = null;
    
    /**
     *
     * @var string 数据库名称
     */
    protected $dbName = '';
    
    /**
     * @var string 数据库配置
     */
    protected $connection = '';
    
    /**
     *
     * @var string 数据表名（不包含表前缀）
     */
    protected $tableName = '';
    
    /**
     *
     * @var string 实际数据表名（包含表前缀）
     */
    protected $trueTableName = '';

    /**
     *
     * @var Query 连贯方法构造对象
     */
    protected $query = null;
    
    /**
     *
     * @var array 命名范围定义
     */
    protected $_scope = array();
    
    protected $insertFields = [];
    
    protected $updateFields = [];

    /**
     * 构造函数
     * 取得DB类的实例对象 字段检查
     * @param string $name 模型名称
     * @param string $tablePrefix 表前缀
     * @param mixed $connection 数据库连接信息
     */
    public function __construct($name = '', $tablePrefix = '', $connection = '')
    {
        parent::__construct($name);

        // 获取模型名称
        if (strpos($name, '.')) {
            // 支持 数据库名.模型名的 定义
            list($this->dbName, $this->name) = explode('.', $name);
        }
        
        // 设置表前缀
        if (is_null($tablePrefix)) {
            // 前缀为Null表示没有前缀
            $this->tablePrefix = '';
        } elseif ('' != $tablePrefix) {
            $this->tablePrefix = $tablePrefix;
        } elseif (!isset($this->tablePrefix)) {
            $this->tablePrefix = C('DB_PREFIX');
        }

        if ($connection) {
            $this->connection = $connection;
        }
        
        // 字段检测
        if (!empty($this->name) && $this->autoCheckFields) {
            $this->getTableSchema();
        }
    }
    
    /**
     * 利用__call方法实现一些特殊的Model方法
     * @param string $method 方法名称
     * @param array $args 调用参数
     * @return mixed
     */
    public function __call($method, $args)
    {
        if (isset($this->_scope[$method])) {
            // 命名范围的单独调用支持
            return $this->scope($method, $args[0]);
        } elseif (strtolower(substr($method, 0, 5)) == 'getby') {
            // 根据某个字段获取记录
            $field         = parse_name(substr($method, 5));
            $where[$field] = $args[0];
            return $this->where($where)->find();
        } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') {
            // 根据某个字段获取记录的某个值
            $name         = parse_name(substr($method, 10));
            $where[$name] = $args[0];
            return $this->where($where)->getField($args[1]);
        } else {
            return parent::__call($method, $args);
        }
    }
    
    /**
     * 得到完整的数据表名
     * 
     * @return string
     */
    public function getTableName()
    {
        if (empty($this->trueTableName)) {
            $tableName = !empty($this->tablePrefix) ? $this->tablePrefix : '';
            if (!empty($this->tableName)) {
                $tableName .= $this->tableName;
            } else {
                $tableName .= parse_name($this->name);
            }
            $this->trueTableName = strtolower($tableName);
        }
        return (!empty($this->dbName) ? $this->dbName . '.' : '') . $this->trueTableName;
    }
    
    /**
     * 
     * @return string 返回表名前缀
     */
    public function getTablePrefix()
    {
        return $this->tablePrefix;
    }
    
    /**
     * 取该模型对应的数据表的字段定义信息
     * getFields方法则仅返回字段列表
     * @return array
     */
    public function getTableSchema()
    {
        // 只在第一次执行记录
        if (empty($this->fields)) {
            $schema = $this->getQuery()->getTableSchema($this->getTableName());
            if ($schema) {
                $this->fields = $schema;
                if (!empty($schema['_pk'])) {
                    $this->pk = $schema['_pk'];
                }
            }
        }
            
        return $this->fields;
    }
    
    /**
     * 创建数据对象 但不保存到数据库
     * 
     * 支持场景:
     * 
     * $this->field('field1,field2...')->create()
     * 
     * @param mixed $data 创建数据
     * @param string $type 状态
     * @param array $fields 状态
     * @return mixed
     */
    public function create($data = '', $type = '', array $fields = [])
    {
        // 如果没有传值默认取POST数据
        if (empty($data)) {
            $data = I('post.');
        }
        
        // 状态
        $type = $type ?: (!empty($data[$this->getPk()]) ? self::MODEL_UPDATE : self::MODEL_INSERT);

        // 检查字段映射
        $data = $this->parseFieldsMap($data, 0);

        // 检测提交字段的合法性
        if ($field_options = $this->getQuery()->getOptions('field')) {
            $fields = $field_options;
            $this->getQuery()->appendOptions(['field' => null]);
        } elseif (self::MODEL_INSERT == $type && !empty($this->insertFields)) {
            $fields = $this->insertFields;
        } elseif (self::MODEL_UPDATE == $type && !empty($this->updateFields)) {
            $fields = $this->updateFields;
            $pk     = $this->getPk();
            if (is_string($pk)) {
                array_push($fields, $pk);
            }
            if (is_array($pk)) {
                foreach ($pk as $pkTemp) {
                    array_push($fields, $pkTemp);
                }
            }
        }
        
        if (is_string($fields)) {
            $fields = explode(',', $fields);
        }
        
        return parent::create($data, $type, $fields);
    }

    /**
     * 获取主键名称
     * 
     * @return string
     */
    public function getPk()
    {
        return $this->pk;
    }
    
    /**
     * 仅返回字段列表,不包含schema
     * @override
     * @return array
     */
    public function getFields()
    {
        $fields = parent::getFields();
        unset($fields['_type'], $fields['_pk']);
        return $fields;
    }
    
    /**
     * 获取数据表字段信息
     * @return array
     */
    public function getDbFields()
    {
        if ($this->getQuery()->getOption('table')) {
            return $this->getQuery()->getDbFields();
        }

        if ($this->fields) {
            return $this->getFields();
        }
        
        return false;
    }
    
    /**
     * 根据验证因子验证字段`
     *
     * @param array $data 创建数据
     * @param array $val 验证因子
     * @return boolean
     */
    protected function validateFieldItem($data, $val)
    {
        switch (strtolower(trim($val[4]))) {
            case 'unique':    // 验证某个值是否唯一
                if (is_string($val[0]) && strpos($val[0], ',')) {
                    $val[0] = explode(',', $val[0]);
                }

                $map = array();
                if (is_array($val[0])) {
                    // 支持多个字段验证
                    foreach ($val[0] as $field) {
                        $map[$field] = $data[$field];
                    }
                } else {
                    $map[$val[0]] = $data[$val[0]];
                }
                $pk = $this->getPk();
                if (!empty($data[$pk]) && is_string($pk)) {
                    // 完善编辑的时候验证唯一
                    $map[$pk] = array('neq', $data[$pk]);
                }
                $options = $this->getQuery()->getOptions();
                if ($this->where($map)->find()) {
                    return false;
                }

                $this->getQuery()->appendOptions($options);
                return true;
            default:
                return parent::validateFieldItem($data, $val);
        }
    }

    /**
     * 生成查询SQL 可用于子查询
     * 
     * @return string
     */
    public function buildSql()
    {
        return '( ' . $this->fetchSql(true)->select() . ' )';
    }

    /**
     * 数据读取后的处理
     * 
     * @param array $data 当前数据
     * @return array
     */
    public function _read_data($data)
    {
        // 检查字段映射
        if (!empty($this->_map) && C('READ_DATA_MAP')) {
            foreach ($this->_map as $key => $val) {
                if (isset($data[$val])) {
                    $data[$key] = $data[$val];
                    unset($data[$val]);
                }
            }
        }
        return $data;
    }
    
    /**
     * 获取数据查询表达式对象
     * @return \DeCMF\Core\Db\Query
     */
    public function getQuery()
    {
        if (empty($this->query)) {
            $this->query = new Query($this, $this->tablePrefix);
            // 数据库初始化操作
            // 获取数据库操作对象
            // 当前模型有独立的数据库连接信息
            $this->query->db(0, $this->connection, true);
        }
        
        return $this->query;
    }

    /**
     * 
     * @param array $options 删除数据前的回调方法
     * @return boolean false 代表停止删除操作
     */
    protected function _before_delete($options)
    {}
    // 删除成功后的回调方法
    protected function _after_delete($data, $options)
    {}
    
    // 查询成功的回调方法
    protected function _after_find(&$result, $options)
    {}

    // 写入数据前的回调方法 包括新增和更新
    protected function _before_write(&$data)
    {}
    
    // 插入数据前的回调方法
    protected function _before_insert(&$data, $options)
    {}
    
    // 插入成功后的回调方法
    protected function _after_insert($data, $options)
    {}
    
    /**
     * 
     * @param array $data
     * @param array $options 更新数据前的回调方法
     * @return boolean false 代表停止更新
     */
    protected function _before_update(&$data, $options)
    {}
    
    // 更新成功后的回调方法
    protected function _after_update($data, $options)
    {}
    
    // 查询成功后的回调方法
    protected function _after_select(&$resultSet, $options)
    {}
    
    /**
     * 查询数据
     * 
     * @param mixed $options 表达式参数
     * @return mixed
     */
    public function find($options = array())
    {
        if (is_numeric($options) || is_string($options)) {
            $where[$this->getPk()] = $options;
            $options               = array();
            $options['where']      = $where;
        }
        // 根据复合主键查找记录
        $pk = $this->getPk();
        if (is_array($options) && (count($options) > 0) && is_array($pk)) {
            // 根据复合主键查询
            $count = 0;
            foreach (array_keys($options) as $key) {
                if (is_int($key)) {
                    $count++;
                }

            }
            if (count($pk) == $count) {
                $i = 0;
                foreach ($pk as $field) {
                    $where[$field] = $options[$i];
                    unset($options[$i++]);
                }
                $options['where'] = $where;
            } else {
                return false;
            }
        }
        
        return $this->getQuery()->appendOptions($options)->find();
    }
    
    public function afterFind(&$data, &$options)
    {
        $this->_after_find($data, $options);
    }
    
    /**
     * 删除数据
     * 
     * @param mixed $options 表达式
     * @return mixed
     */
    public function delete($options = array())
    {
        $pk = $this->getPk();
        if (empty($options) 
            && empty($this->getQuery()->getOption('where'))) {
            // 如果删除条件为空 则删除当前数据对象所对应的记录
            if (!empty($this->data) && isset($this->data[$pk])) {
                return $this->delete($this->data[$pk]);
            } else {
                return false;
            }

        }
        
        if (is_numeric($options) || is_string($options)) {
            // 根据主键删除记录
            if (strpos($options, ',')) {
                $where[$pk] = array('IN', $options);
            } else {
                $where[$pk] = $options;
            }
            $options = array();
            $options['where'] = $where;
        }
        
        // 根据复合主键删除记录
        if (is_array($options) && (count($options) > 0) && is_array($pk)) {
            $count = 0;
            foreach (array_keys($options) as $key) {
                if (is_int($key)) {
                    $count++;
                }

            }
            if (count($pk) == $count) {
                $i = 0;
                foreach ($pk as $field) {
                    $where[$field] = $options[$i];
                    unset($options[$i++]);
                }
                $options['where'] = $where;
            } else {
                return false;
            }
        }
        
        $result = $this->getQuery()->appendOptions($options)->delete();
        
        if (false !== $result && is_numeric($result)) {
            $data = array();
            if ($where = $this->getQuery()->getOption('where') 
                 && !is_null($where[$pk])) {
                $data[$pk] = $where[$pk];
            }

            $this->_after_delete($data, $options);
        }
        
        return $result;
    }
    
    /**
     * 供query调用的option过滤方法
     * @param array $options
     * @return boolean false代表终止执行
     */
    public function beforeDelete(&$options)
    {
        return $this->_before_delete($options);
    }
    
    /**
     * 查询数据集
     * 
     * @param array $options 表达式参数
     * @return mixed
     */
    public function select($options = array())
    {
        $pk = $this->getPk();
        if (is_string($options) || is_numeric($options)) {
            // 根据主键查询
            if (strpos($options, ',')) {
                $where[$pk] = array('IN', $options);
            } else {
                $where[$pk] = $options;
            }
            $options          = array();
            $options['where'] = $where;
        } elseif (is_array($options) && (count($options) > 0) && is_array($pk)) {
            // 根据复合主键查询
            $count = 0;
            foreach (array_keys($options) as $key) {
                if (is_int($key)) {
                    $count++;
                }

            }
            if (count($pk) == $count) {
                $i = 0;
                foreach ($pk as $field) {
                    $where[$field] = $options[$i];
                    unset($options[$i++]);
                }
                $options['where'] = $where;
            } else {
                return false;
            }
        } elseif (false === $options) {
            // 用于子查询 不查询只返回SQL
            $options['fetch_sql'] = true;
        }
        
        return $this->getQuery()->appendOptions($options)->select();
    }
    
    /**
     * 对结果进行处理
     * 结果为数组且不为空才会被触发,从缓存中读取也不会被触发
     * @param array $resultSet
     * @param array $options
     */
    public function afterSelect(&$resultSet, &$options)
    {
        if (!empty($resultSet)) {
            
            $resultSet = array_map(array($this, '_read_data'), $resultSet);
            $this->_after_select($resultSet, $options);
            
            if (isset($options['index'])) {
                // 对数据集进行索引
                $index = explode(',', $options['index']);
                foreach ($resultSet as $result) {
                    $_key = $result[$index[0]];
                    if (isset($index[1]) && isset($result[$index[1]])) {
                        $cols[$_key] = $result[$index[1]];
                    } else {
                        $cols[$_key] = $result;
                    }
                }
                $resultSet = $cols;
            }
        }
    }
    
    /**
     * 新增数据
     * 
     * @param mixed $data 数据
     * @param array $options 表达式
     * @param boolean $replace 是否replace
     * @return mixed
     */
    public function add($data = '', $options = array(), $replace = false)
    {
        if (empty($data)) {
            // 没有传递数据，获取当前数据对象的值
            if (!empty($this->data)) {
                $data = $this->data;
                // 重置数据
                $this->data = array();
            } else {
                $this->error = L('_DATA_TYPE_INVALID_');
                return false;
            }
        }
        // 数据处理
        $data = $this->_facade($data);
        
        return $this->getQuery()->appendOptions($options)->insert($data, $replace);
    }
    
    public function beforeInsert(&$data, &$options)
    {
        return $this->_before_insert($data, $options);
    }
    
    /**
     * 供query回调
     * @param int|boolean $result 插入结果
     * @param type $options
     * @return boolean|int
     */
    public function afterInsert(&$result, &$options)
    {
        if (false !== $result && is_numeric($result)) {
            $pk = $this->getPk();
            // 增加复合主键支持
            if (is_array($pk)) {
                return $result;
            }

            $insertId = $this->getQuery()->getLastInsID();
            if ($insertId) {
                // 自增主键返回插入ID
                $data[$pk] = $insertId;
                if (false === $this->_after_insert($data, $options)) {
                    return false;
                }
                return $insertId;
            }
            if (false === $this->_after_insert($data, $options)) {
                return false;
            }
        }
        
        return $result;
    }
    
    /**
     * 保存数据
     * 
     * @param mixed $data 数据
     * @param array $options 表达式
     * @return boolean
     */
    public function save($data = '', $options = array())
    {
        if (empty($data)) {
            // 没有传递数据，获取当前数据对象的值
            if (!empty($this->data)) {
                $data = $this->data;
                // 重置数据
                $this->data = array();
            } else {
                $this->error = L('_DATA_TYPE_INVALID_');
                return false;
            }
        }
        // 数据处理
        $data = $this->_facade($data);
        if (empty($data)) {
            // 没有数据则不执行
            $this->error = L('_DATA_TYPE_INVALID_');
            return false;
        }
        
        $result = $this->getQuery()->appendOptions($options)->update($data);
        
        if (false !== $result && is_numeric($result)) {
            $pk = $this->getPk();
            if (isset($options['where'][$pk])) {
                $data[$pk] = $options['where'][$pk];
            }

            $this->_after_update($data, $options);
        }
        
        return $result;
    }
    
    /**
     * 供query调用的option过滤方法
     * @param array $options
     * @param array $data
     * @return boolean false代表终止执行
     */
    public function beforeUpdate(&$options, &$data)
    {
        if (!isset($options['where'])) {
            
            $pk = $this->getPk();
            // 如果存在主键数据 则自动作为更新条件
            if (is_string($pk) && isset($data[$pk])) {
                $where[$pk] = $data[$pk];
                unset($data[$pk]);
            } elseif (is_array($pk)) {
                // 增加复合主键支持
                foreach ($pk as $field) {
                    if (isset($data[$field])) {
                        $where[$field] = $data[$field];
                    } else {
                        // 如果缺少复合主键数据则不执行
                        $this->error = L('_OPERATION_WRONG_');
                        return false;
                    }
                    unset($data[$field]);
                }
            }
            
            if (!isset($where)) {
                // 如果没有任何更新条件则不执行
                return false;
            } else {
                $options['where'] = $where;
            }
        }
        
        return $this->_before_update($data, $options);
    }
    
    /**
     * 指定当前的数据表
     * 
     * @param mixed $table
     * @return $this
     */
    public function table($table)
    {
        $this->getQuery()->table($table);
        
        return $this;
    }
    
    /**
     * 指定查询字段 支持字段排除
     * 
     * @param mixed $field
     * @param boolean $except 是否排除
     * @return $this
     */
    public function field($field, $except = false)
    {
        if (true === $field) {
            // 获取全部字段
            $fields = $this->getFields();
            $field  = $fields ?: '*';
        } elseif ($except) {
            // 字段排除
            if (is_string($field)) {
                $field = explode(',', $field);
            }
            $fields = $this->getFields();
            $field  = $fields ? array_diff($fields, $field) : $field;
        }
        
        $this->getQuery()->field($field);
        
        return $this;
    }
    
    /**
     * 通过Select方式添加记录
     * 
     * @param string $fields 要插入的数据表字段名
     * @param string $table 要插入的数据表名
     * @param array $options 表达式
     * @return boolean
     */
    public function selectAdd($fields = '', $table = '', $options = array())
    {
        $result = $this->getQuery()->appendOptions($options)->selectAdd($fields, $table);
        if (false === $result) {
            // 数据库插入操作失败
            $this->error = L('_OPERATION_WRONG_');
        }
        
        return $result;
    }
    
    public function addAll($dataList, $options = array(), $replace = false)
    {
        if (empty($dataList)) {
            $this->error = L('_DATA_TYPE_INVALID_');
            return false;
        }
        
        return $this->getQuery()->appendOptions($options)->addAll($dataList, $replace);
    }
    
    /**
     * 获取一条记录的某个字段值
     * 
     * @param string $field  字段名
     * @param string $sepa  字段数据间隔符号 NULL返回数组
     * @return mixed
     */
    public function getField($field, $sepa = null)
    {
        return $this->getQuery()->getField($field, $sepa);
    }
    
    /**
     * 设置记录的某个字段值
     * 支持使用数据库字段和方法
     * 
     * @param string|array $field  字段名
     * @param string $value  字段值
     * @return boolean
     */
    public function setField($field, $value = '')
    {
        return $this->getQuery()->setField($field, $value);
    }
    
    /**
     * 字段值增长
     * 
     * @param string $field  字段名
     * @param integer $step  增长值
     * @param integer $lazyTime  延时时间(s)
     * @return boolean
     */
    public function setInc($field, $step = 1, $lazyTime = 0)
    {
        return $this->getQuery()->setInc($field, $step, $lazyTime);
    }
    
    /**
     * 字段值减少
     * 
     * @param string $field  字段名
     * @param integer $step  减少值
     * @param integer $lazyTime  延时时间(s)
     * @return boolean
     */
    public function setDec($field, $step = 1, $lazyTime = 0)
    {
        return $this->getQuery()->setInc($field, $step, $lazyTime);
    }
    
    /**
     * 指定查询条件 支持安全过滤
     * 
     * @param array|string|object $where 条件表达式
     * @param mixed $parse 预处理参数
     * @return $this
     */
    public function where($where, $parse = null)
    {
        if (!is_null($parse) && is_string($where)) {
            if (!is_array($parse)) {
                $parse = func_get_args();
                array_shift($parse);
            }
            $parse = array_map(array($this->db, 'escapeString'), $parse);
            $where = vsprintf($where, $parse);
        }
        
        $this->getQuery()->where($where);

        return $this;
    }
    
    /**
     * 指定查询数量
     * 
     * @param mixed $offset 起始位置
     * @param mixed $length 查询数量
     * @return $this
     */
    public function limit($offset, $length = null)
    {
        $this->getQuery()->limit($offset, $length);
        
        return $this;
    }
    
    /**
     * 指定分页
     * 
     * @param mixed $page 页数
     * @param mixed $listRows 每页数量
     * @return $this
     */
    public function page($page, $listRows = null)
    {
        $this->getQuery()->page($page, $listRows);
        
        return $this;
    }
    
    /**
     * 查询注释
     * 
     * @param string $comment 注释
     * @return $this
     */
    public function comment($comment)
    {
        $this->getQuery()->comment($comment);
        
        return $this;
    }

    /**
     * 获取执行的SQL语句
     * 
     * @param boolean $fetch 是否返回sql
     * @return $this
     */
    public function fetchSql($fetch = true)
    {
        $this->getQuery()->fetchSql($fetch);
        
        return $this;
    }
    
    /**
     * 构造排序条件
     * 
     * @param string $value
     * @return $this
     */
    public function order($value)
    {
        $this->getQuery()->order($value);
        return $this;
    }
    
    /**
     * 构造strict条件
     * 
     * @param string $value
     * @return $this
     */
    public function strict($value)
    {
        $this->getQuery()->strict($value);
        return $this;
    }
    
    /**
     * 构造别名条件
     * 
     * @param string $value
     * @return $this
     */
    public function alias($value)
    {
        $this->getQuery()->alias($value);
        return $this;
    }
    
    /**
     * 构造having条件
     * 
     * @param string $value
     * @return $this
     */
    public function having($value)
    {
        $this->getQuery()->having($value);
        return $this;
    }
    
    /**
     * 构造group条件
     * 
     * @param string $value
     * @return $this
     */
    public function group($value)
    {
        $this->getQuery()->group($value);
        return $this;
    }
    
    /**
     * 构造lock条件
     * 
     * @param string $value
     * @return $this
     */
    public function lock($value)
    {
        $this->getQuery()->lock($value);
        return $this;
    }
    
    /**
     * 构造distinct条件
     * 
     * @param string $value
     * @return $this
     */
    public function distinct($value)
    {
        $this->getQuery()->distinct($value);
        return $this;
    }
    
    /**
     * 构造filter条件
     * 
     * @param string $value
     * @return $this
     */
    public function filter($value)
    {
        $this->getQuery()->filter($value);
        return $this;
    }
    
    /**
     * 构造result条件
     * 
     * @param string $value
     * @return $this
     */
    public function result($value)
    {
        $this->getQuery()->result($value);
        return $this;
    }
    
    /**
     * 构造index条件
     * 
     * @param string $value
     * @return $this
     */
    public function index($value)
    {
        $this->getQuery()->index($value);
        return $this;
    }
    
    /**
     * 构造force条件
     * 
     * @param string $value
     * @return $this
     */
    public function force($value)
    {
        $this->getQuery()->force($value);
        return $this;
    }
    
    /**
     * 构造master条件
     * 
     * @param string $value
     * @return $this
     */
    public function master($value)
    {
        $this->getQuery()->master($value);
        return $this;
    }
    
    /**
     * 查询SQL组装 join,表名前缀由getRawTableName处理
     * 
     * @param string|array $join
     * @param string $type JOIN类型
     * @return $this
     */
    public function join($join, $type = 'INNER')
    {
        $this->getQuery()->join($join, $type);
        
        return $this;
    }
    
    /**
     * 查询SQL组装 union
     * 
     * @param mixed $union
     * @param boolean $all
     * @return $this
     */
    public function union($union, $all = false)
    {
        $this->getQuery()->union($union, $all);
        
        return $this;
    }
    
    /**
     * USING支持 用于多表删除
     * 
     * @param string|array $using
     * @return $this
     */
    public function using($using)
    {
        $this->getQuery()->using($using);
        
        return $this;
    }
    
    /**
     * 参数绑定,支持动态参数
     * 
     * @param string $key  参数名
     * @param mixed $value  绑定的变量及绑定参数
     * @return $this
     */
    public function bind($key, $value = false)
    {
        $this->getQuery()->bind($key, $value);
        
        return $this;
    }
    
    /**
     * 查询缓存
     * 
     * @param mixed $key
     * @param integer $expire
     * @param string $type
     * @return $this
     */
    public function cache($key = true, $expire = null, $type = '')
    {
        $this->getQuery()->cache($key, $expire, $type);
        
        return $this;
    }
    
    /**
     * count统计
     * @param string $field
     * @return int
     */
    public function count($field = '')
    {
        return $this->getQuery()->count($field);
    }
    
    /**
     * sum查询
     * @param string $field
     * @return int
     */
    public function sum($field = '')
    {
        return $this->getQuery()->sum($field);
    }
    
    /**
     * min查询
     * @param string $field
     * @return int
     */
    public function min($field = '')
    {
        return $this->getQuery()->min($field);
    }
    
    /**
     * max查询
     * @param string $field
     * @return int
     */
    public function max($field = '')
    {
        return $this->getQuery()->max($field);
    }
    
    /**
     * avg查询
     * @param string $field
     * @return int
     */
    public function avg($field = '')
    {
        return $this->getQuery()->avg($field);
    }
    
    /**
     * SQL查询
     * 
     * @param string $sql  SQL指令
     * @param mixed $parse  是否需要解析SQL
     * @return mixed
     */
    public function query($sql, $parse = false)
    {
        return $this->getQuery()->query($sql, $parse);
    }
    
    /**
     * 执行SQL语句
     * 
     * @param string $sql  SQL指令
     * @param mixed $parse  是否需要解析SQL
     * @return false | integer
     */
    public function execute($sql, $parse = false)
    {
        return $this->getQuery()->execute($sql, $parse);
    }
    
    /**
     * 存储过程返回多数据集
     * 
     * @param string $sql  SQL指令
     * @param mixed $parse  是否需要解析SQL
     * @return array
     */
    public function procedure($sql, $parse = false)
    {
        return $this->getQuery()->procedure($sql, $parse);
    }
    
    /**
     * 调用命名范围
     *
     * @param mixed $scope 命名范围名称 支持多个 和直接定义
     * @param array $args 参数
     * @return $this
     */
    public function scope($scope = '', $args = null)
    {
        if ('' === $scope) {
            if (isset($this->_scope['default'])) {
                // 默认的命名范围
                $options = $this->_scope['default'];
            } else {
                return $this;
            }
        } elseif (is_string($scope)) {
            // 支持多个命名范围调用 用逗号分割
            $scopes = explode(',', $scope);
            $options = array();
            foreach ($scopes as $name) {
                if (!isset($this->_scope[$name])) {
                    continue;
                }

                $options = array_merge($options, $this->_scope[$name]);
            }
            if (!empty($args) && is_array($args)) {
                $options = array_merge($options, $args);
            }
        } elseif (is_array($scope)) {
            // 直接传入命名范围定义
            $options = $scope;
        }

        if (is_array($options) && !empty($options)) {
            $this->getQuery()->appendOptions(array_change_key_case($options));
        }
        return $this;
    }

    /**
     * 对保存到数据库的数据进行处理
     * 
     * @param mixed $data 要操作的数据
     * @return boolean
     */
    protected function _facade($data)
    {
        $data = $this->getQuery()->facade($data, $this->getTableSchema());
        
        $this->_before_write($data);
        return $data;
    }
    
    // 表达式过滤回调方法
    public function _options_filter(&$options)
    {}
    
    /**
     * 解析SQL语句
     * 
     * @param string $sql  SQL指令
     * @param boolean $parse  是否需要解析SQL
     * @return string
     */
    protected function parseSql($sql, $parse)
    {
        return $this->getQuery()->parseSql($sql, $parse);
    }
    
    /**
     * 返回最后执行的sql语句
     * 
     * @return string
     */
    public function getLastSql()
    {
        return $this->getQuery()->getLastSql();
    }
    
    /**
     * 鉴于getLastSql比较常用 增加_sql 别名
     * @deprecated since version 1.5
     * @return string
     */
    public function _sql()
    {
        return $this->getLastSql();
    }
    
    
    /**
     * 启动事务
     * 
     * @return void
     */
    public function startTrans()
    {
        return $this->getQuery()->startTrans(); 
    }

    /**
     * 提交事务
     * 
     * @return boolean
     */
    public function commit()
    {
        return $this->getQuery()->commit();
    }

    /**
     * 事务回滚
     * 
     * @return boolean
     */
    public function rollback()
    {
        return $this->getQuery()->rollback();
    }

    /**
     * 返回数据库的错误信息
     * 
     * @return string
     */
    public function getDbError()
    {
        return $this->getQuery()->getError();
    }

    /**
     * 返回最后插入的ID
     * 
     * @return string
     */
    public function getLastInsID()
    {
        return $this->getQuery()->getLastInsID();
    }

}

